﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace ChessEngine
{
    public class ChessModel
    {
        public int SizeX { get { return game.Board.XSize; } }
        public int SizeY { get { return game.Board.YSize; } }

        private ChessGame game; // TODO private
        public ChessView GameLook { get { return (ChessView) game; } }

        public bool[,] Hilited { get; private set; }
        public Coordinate Selection { get; private set; }

        public ChessModel ()
        {
            game = new ChessGame ();
            Hilited = new bool[SizeX, SizeY];
        }


        public void CreateGame ()
        {
            // TODO reinit instead
            Hilited = new bool[SizeX, SizeY];
            game.SetupStandard ();
            game.OnGameCreated ((ChessView) game);
            game.OnStatusUpdate ();
        }


        public bool IsMovable (int x, int y)
        {
            return game.Board[x, y].IsPlayer (game.PlayerTurn);
        }


        public void ClearSelection ()
        {
            if (HasSelection)
            {
                Hilited[Selection.X, Selection.Y] = false;
                HasSelection = false;
                GameLook.OnHiliteChange (Selection.X, Selection.Y, false);
            }
        }

        public bool SetSelection (int newX, int newY)
        {
            if (HasSelection && Selection.X != newX && Selection.Y != newY)
                ClearSelection ();

            HasSelection = true;
            Selection = new Coordinate (newX, newY);
            Hilited[Selection.X, Selection.Y] = true;
            GameLook.OnHiliteChange (newX, newY, true);
            return false;
        }


        public void ClearHilites ()
        {
            for (int y = 0; y < 8; ++y)
                for (int x = 0; x < 8; ++x)
                    if (Hilited[x, y])
                        setHilite (x, y, false);
        }

        private void setHilite (int x, int y, bool newValue)
        {
            if (Hilited[x, y] != newValue)
            {
                Hilited[x, y] = newValue;
                GameLook.OnHiliteChange (x, y, newValue);
            }
        }


        public void BeginShowMoves (int x, int y)
        {
            ClearSelection ();

            Piece piece = game.Board[x, y] as Piece;
            if (piece != null)
                foreach (Coordinate place in piece.LegalMoves)
                    setHilite (place.X, place.Y, true);
        }

        public void EndShowMoves ()
        {
            ClearHilites ();
        }


        public bool MakeMove (int fromX, int fromY, int toX, int toY)
        {
            try
            {
                bool isIllegal = game.Move (fromX, fromY, toX, toY);
                if (isIllegal)
                {
                    // The UI should never encounter this.
                    GameLook.OnMessageShow ("Invalid move", "Sorry");
                    return true;
                }
            }
            catch (ArgumentException ex)
            {
                // The UI should never encounter this.
                GameLook.OnMessageShow (ex.Message, "Sorry");
                return true;
            }

            Ply ply = game.Plies.Top;

            GameLook.OnTokenRemove (fromX, fromY);

            if (ply.IsPawnPromotion)
                GameLook.OnTokenPlace (toX, toY, ply.Moving.Player.Index, ply.Promotion.Index);
            else
            {
                GameLook.OnTokenPlace (toX, toY, ply.Moving.Player.Index, ply.Moving.Index);
                if (ply.IsCaptureEnPassant)
                    GameLook.OnTokenRemove (toX, fromY);
                else if (ply.IsQueensideCastle)
                {
                    GameLook.OnTokenRemove (0, fromY);
                    GameLook.OnTokenPlace (toX + 1, toY, ply.Moving.Player.Index, Rook.StaticIndex);
                }
                else if (ply.IsKingsideCastle)
                {
                    GameLook.OnTokenRemove (game.Board.YSize - 1, fromY);
                    GameLook.OnTokenPlace (toX - 1, toY, ply.Moving.Player.Index, Rook.StaticIndex);
                }
            }

            ClearSelection ();
            game.OnPieceMoved ();
            game.OnStatusUpdate ();

            return false;
        }


        public bool UndoMove ()
        {
            if (!GameLook.IsUnderway || GameLook.Plies.Count == 0)
                return true;

            Ply ply = game.Plies.Top;
            int playerIndex = ply.Moving.Player.Index;

            if (ply.Captured == null)
                GameLook.OnTokenRemove (ply.To.X, ply.To.Y);
            else
            {
                int y;
                if (ply.IsCaptureEnPassant)
                {
                    y = ply.From.Y;
                    GameLook.OnTokenRemove (ply.To.X, ply.To.Y);
                }
                else
                    y = ply.To.Y;

                GameLook.OnTokenPlace (ply.To.X, y, ply.Captured.Player.Index, ply.Captured.Index);
            }

            int token;
            if (ply.Promotion != null)
                token = Pawn.StaticIndex;
            else
                token = ply.Moving.Index;

            GameLook.OnTokenPlace (ply.From.X, ply.From.Y, playerIndex, token);

            if (ply.Moving is King)
            {
                if (ply.IsQueensideCastle)
                {
                    GameLook.OnTokenPlace (0, ply.To.Y, playerIndex, Rook.StaticIndex);
                    GameLook.OnTokenRemove (ply.To.X + 1, ply.To.Y);
                }
                else if (ply.IsKingsideCastle)
                {
                    GameLook.OnTokenPlace (GameLook.Board.XSize - 1, ply.To.Y, playerIndex, Rook.StaticIndex);
                    GameLook.OnTokenRemove (ply.To.X - 1, ply.To.Y);
                }
            }

            game.OnUndoPreview ();
            game.Undo ();
            game.OnStatusUpdate ();

            return false;
        }


        public void SetPromotion (string newPieceName)
        {
            Player player = game.PlayerTurn;

            if (newPieceName.Equals (typeof (Queen).Name, StringComparison.OrdinalIgnoreCase))
                game.SetPromotion (player, typeof (Queen));
            else if (newPieceName.Equals (typeof (Rook).Name, StringComparison.OrdinalIgnoreCase))
                game.SetPromotion (player, typeof (Rook));
            else if (newPieceName.Equals (typeof (Bishop).Name, StringComparison.OrdinalIgnoreCase))
                game.SetPromotion (player, typeof (Bishop));
            else if (newPieceName.Equals (typeof (Knight).Name, StringComparison.OrdinalIgnoreCase))
                game.SetPromotion (player, typeof (Knight));
            else
                throw new ArgumentException ("Unknown promotion '" + newPieceName + @"'");
        }


        public void Resign ()
        {
            try
            {
                game.ResignGame ();
                //UpdateStatus ();
                //game.OnStatusUpdate ();
            }
            catch (ArgumentException ex)
            {
                game.OnMessageShow (ex.Message, "Sorry");
            }
        }


        public void RequestDraw ()
        {
            try
            {
                game.DrawGame ();
                //CUT game.OnStatusUpdate ();
            }
            catch (ArgumentException ex)
            {
                game.OnMessageShow (ex.Message, "Sorry");
            }
        }


        public bool HasSelection { get; private set; }
    }
}
